package me.zhenqu.redis;

import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Created at 11-7-5
 *
 * @author yonghui.chen
 */
public class MonitorJedisPool {
    private JedisPool mainPool;
    private String server;
    private JedisPool rwPool;

    public MonitorJedisPool(final String mainServer, final int mainPort,
                            String rwServer, int rwServerPort) {

        this.server = mainServer + ":" + mainPort;
        GenericObjectPool.Config poolConfig = new JedisPoolConfig();

        poolConfig.maxActive = 20;
        poolConfig.maxIdle = 5;
        poolConfig.minIdle = 1;
        poolConfig.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW;
        poolConfig.timeBetweenEvictionRunsMillis = 60000;
        poolConfig.numTestsPerEvictionRun = 10;
        poolConfig.maxWait = 3000;
        mainPool = new JedisPool(poolConfig, mainServer, mainPort);

        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
            @Override
            public void run() {
                mainPool.destroy();
            }
        }));
        if (rwServer != null) {
            this.rwPool = new JedisPool(poolConfig, rwServer, rwServerPort);
            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                @Override
                public void run() {
                    rwPool.destroy();
                }
            }));
        }
    }

    private Logger logger = Logger.getLogger(getClass());
    private JedisPool active;

    public Jedis getResource() {
        return getResource(true);
    }

    public Jedis getResource(boolean again) {
        try {
            active = rwPool != null && active == mainPool ? rwPool : mainPool;
            PooledJedis jedis = active.getResource();
            logger.debug("Get a resource " + jedis);
            jedis.setIdle(false);
            return jedis;
        } catch (RuntimeException e) {
            logger.error("Cannot get a resource from pool " + server, e);
            System.err.println("get connection error from " + server);
            e.printStackTrace();
            if (again) return getResource(false);
            else throw e;
        }
    }

    public void returnResource(Jedis jedis) {
        try {
            PooledJedis j = (PooledJedis) jedis;
            if (j.getKey().equals(server)) {
                mainPool.returnResource(j);
            } else {
                rwPool.returnResource(j);
            }
            j.setIdle(true);
            logger.debug("Return a resource " + jedis);
        } catch (Exception e) {
            logger.warn("Error return " + jedis, e);
            returnBrokenResource(jedis);
            jedis.disconnect();
        }
    }

    public void returnBrokenResource(Jedis jedis) {
        try {
            PooledJedis j = (PooledJedis) jedis;
            logger.debug("Return a broken resource " + jedis);
            if (j.getKey().equals(server)) {
                mainPool.returnBrokenResource(j);
            } else {
                rwPool.returnBrokenResource(j);
            }
        } catch (Exception e) {
            logger.warn("Error return " + jedis, e);
        }
    }

    public void check() {
    }

    public Jedis getReadWriteResource() {
        if (rwPool == null) return getResource();
        try {
            PooledJedis jedis = rwPool.getResource();
            logger.debug("Get a resource " + jedis);
            jedis.setIdle(false);
            return jedis;
        } catch (RuntimeException e) {
            logger.error("Cannot get a resource from pool " + server, e);
            e.printStackTrace();
            throw e;
        }
    }
}
